package com.lfy.ch05;

/**
 * 解决资源竞争的多线程
 */
public class SaleTicketSafe {

    private int ticketNum = 100;


    // synchronized： 同步； JVM在底层，对这个方法执行的时候会加锁。
    //1、多线程执行这个方法。每个线程都需要先抢锁
    //2、拿到锁的线程，去执行内部方法
    //3、没有拿到锁（被别人锁住了[别人正在执行呢]）的线程，会在外面阻塞等待。
    //4、拿到锁的线程，执行完之后会释放锁。
    //5、其他线程可以继续抢锁了。抢到的人在去执行里面的代码
    public synchronized void saleTicket() {
       while (ticketNum > 0){
           try {
               Thread.sleep(20);
           }catch (InterruptedException e){
           }
           System.out.println(Thread.currentThread().getName() + " 卖出一张：还剩" + --ticketNum);
       }

        System.out.println(Thread.currentThread().getName() + "票卖完了！");
    }


    //改进： 每次就卖一张票，就释放锁。

    /**
     * synchronized：
     * 1、写到代码块上： 你传的是什么，就用这个当成锁，自己保证锁的唯一性。
     * 2、写到方法上：（谁掉了这个方法锁就是谁）
     *     1)、实例方法： 对象.方法()； 锁就是当前对象； safe.saleOneTicket(); 锁就是safe；也就是this
     *     2)、静态方法： 类/对象.静态方法() ==底层==> 类.方法()；
     *           锁就是当前类
     *
     *           实例变量： int a = 1;
     *           静态变量： static int a = 1;
     *           实例方法： void aaa(){}；
     *           静态方法： static void aaa(){}；
     *           实例都看对象，静态都看类。
     */
    public void saleOneTicket(){
        // synchronized (锁对象) ： 把谁当成锁; Java规定，任何对象（基本数据类型不行）都能当锁。
        // new Object()： 锁不住，因为每次都是一个新的对象。（新锁）

        //Object.class: 拿到 Object 类

        // 保证 多个线程用的都是同一个即可; 写法：
        //1、Xxx.class； 由于类在JVM中只有一个，所以用这个类作为锁。
        //2、this： 代表当前对象; 谁调用saleOneTicket，就是谁。
        //3、其他都行，但是必须是同一个对象。
        synchronized (this){  // safe.saleOneTicket(); this 就代表 safe 这个对象
            if (ticketNum > 0){
                try {
                    Thread.sleep(20);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println(Thread.currentThread().getName() + "卖出一张：还剩" + --ticketNum);
            }else {
                System.out.println(Thread.currentThread().getName() + "票卖完了！");
            }
        }

    }
}
